package yo.latyoo.hokage.zetsu.executor;

import jakarta.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import yo.latyoo.hokage.tools.consts.LogConst;
import yo.latyoo.hokage.zetsu.handler.ZetsuHandler;
import yo.latyoo.hokage.zetsu.handler.annotation.ZetsuJob;

import java.lang.reflect.Method;
import java.util.Map;

import static yo.latyoo.hokage.tools.consts.LogConst.PREFIX_ZETSU;

public class ZetsuSpringExecutor extends ZetsuExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {
    private static final Logger logger = LoggerFactory.getLogger(ZetsuSpringExecutor.class);

    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
        ZetsuSpringExecutor.applicationContext = applicationContext;
    }

    @Override
    public void afterSingletonsInstantiated() {
        logger.info(PREFIX_ZETSU+" starting......");
        registerZetsuJob(ZetsuSpringExecutor.applicationContext);
        logger.info(PREFIX_ZETSU+"tart success......");
    }


    private void  registerZetsuJob(ApplicationContext applicationContext){
        if (applicationContext == null)
            return;
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        for (String beanName : beanDefinitionNames){
            Object bean = applicationContext.getBean(beanName);
            Map<Method, ZetsuJob> annotatedMethods = null;   // referred to ：org.springframework.context.event.EventListenerMethodProcessor.processBean
            try {
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        (MethodIntrospector.MetadataLookup<ZetsuJob>) method -> AnnotatedElementUtils.findMergedAnnotation(method, ZetsuJob.class));
            } catch (Throwable ex) {
                logger.error(PREFIX_ZETSU + "method resolve error for bean[" + beanName + "].", ex);
            }
            if (annotatedMethods==null || annotatedMethods.isEmpty()) {
                continue;
            }

            for (Map.Entry<Method, ZetsuJob> methodJobEntry : annotatedMethods.entrySet()) {
                Method executeMethod = methodJobEntry.getKey();
                ZetsuJob jobHandlerMethod = methodJobEntry.getValue();
                // register
                putJobMethodHandler(jobHandlerMethod, bean, executeMethod);
            }
        }
    }

    @Override
    public void destroy() throws Exception {

    }
}
